/* Copyright (C) 2011 Fredrique Samuels, fredriquesamuels@gmail.com 

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include "simulation_entity.h"
#include "sim_scene.h"

namespace tri{

TSimulationEntity::TSimulationEntity(NewtonWorld* world, int id): m_GraphicComponent(NULL),
	m_PhysicsComponent(NULL),
	m_PhysicsModelComponent(NULL),
	m_PhysicsModel(PHYSICS_BOX),
	m_NewtonWorld(NULL),
	m_OwnerOfGraphics(false),
	m_IgnoreRotation(false),
	mID(id),
	m_Parent(NULL){
	
}

TSimulationEntity::~TSimulationEntity(){
	clearPhysics();
	clearGraphics();
}

void TSimulationEntity::setIgnoreRotation(bool b){
	m_IgnoreRotation = b;
}

bool TSimulationEntity::ignoreRotation(){
	return m_IgnoreRotation;
}

void TSimulationEntity::setGraphicComponent(tri::TEntity* ent, int modelType, float mass, bool owner){
	clearGraphics();
	m_GraphicComponent = ent;
	m_Mass = mass;
	m_PhysicsModel = modelType;
	m_OwnerOfGraphics = owner;
	initPhysics();
}


tri::TEntity* TSimulationEntity::getGraphicsComponent(){
	return m_GraphicComponent;
}

tri::TEntity* TSimulationEntity::getPhysicsModelComponent(){return m_PhysicsModelComponent;}

void TSimulationEntity::setWorld(NewtonWorld* world){
	clearPhysics();
	m_NewtonWorld = world;
	initPhysics();
}

void TSimulationEntity::initPhysics(){
	if (m_GraphicComponent == NULL || m_NewtonWorld == NULL){
		return;
	}
	
	NewtonCollision* shape=NULL;
		
	switch(m_PhysicsModel){
	case PHYSICS_BOX:
		//tri::LOG_INFO("Creating Box Physics Model..");
		// add a body with a box shape
		shape = tri::TCreateNewtonBox (m_NewtonWorld,m_GraphicComponent->getSize(), mID);
		if (m_PhysicsModelComponent!=NULL){
			delete m_PhysicsModelComponent;
		}
		m_PhysicsModelComponent = new tri::TCube();
		m_PhysicsModelComponent->setSize(m_GraphicComponent->getSize());
		m_PhysicsModelComponent->setWireFrameMode(true);
		break;
	case PHYSICS_SPHERE:
		//tri::LOG_INFO("Creating Sphere Physics Model..");
		// add a body with a box shape
		shape = tri::TCreateNewtonSphere (m_NewtonWorld,m_GraphicComponent->getSize(), mID);
		if (m_PhysicsModelComponent!=NULL){
			delete m_PhysicsModelComponent;
		}
		m_PhysicsModelComponent = new tri::TSphere();
		m_PhysicsModelComponent->setSize(m_GraphicComponent->getSize());
		m_PhysicsModelComponent->setWireFrameMode(true);
		break;
	case PHYSICS_CYLINDER:
		shape = tri::TCreateNewtonCylinder (m_NewtonWorld,m_GraphicComponent->getSize(), mID);
		if (m_PhysicsModelComponent!=NULL){
			delete m_PhysicsModelComponent;
		}
		m_PhysicsModelComponent = new tri::TCube();
		m_PhysicsModelComponent->setSize(m_GraphicComponent->getSize());
		m_PhysicsModelComponent->setWireFrameMode(true);
	}
	
	if (shape != NULL){
		m_PhysicsComponent = tri::TCreateRigidBody(m_NewtonWorld, this, 
									shape, m_Mass);
	}
}

NewtonBody* TSimulationEntity::getPhysicsBody(){

	return m_PhysicsComponent;
}

void TSimulationEntity::setTransformation(tri::TVector rot, tri::TVector pos){
	NewtonBody* body = NULL;
	body  = getPhysicsBody();
	if (body != NULL && m_GraphicComponent != NULL){
		tri::TSetBodyMatrix(body, rot, pos);
		m_GraphicComponent->setRotation(rot*-1);
		m_GraphicComponent->setPosition(pos);
		m_PhysicsModelComponent->setRotation(rot*-1);
		m_PhysicsModelComponent->setPosition(pos);
	}
}


void TSimulationEntity::setMaterialGroup(int group){
	NewtonBody* body = NULL;
	body  = getPhysicsBody();
	if (body != NULL && group != -1){
		// assign an Material ID to this body
		NewtonBodySetMaterialGroupID (body, group);
	}
}

float TSimulationEntity::getMass(){
	return m_Mass;
}

void TSimulationEntity::applyForce(tri::TVector f){
	m_TotalForce+=f;
}

void TSimulationEntity::setForce(tri::TVector f){
	m_TotalForce = f;
}

tri::TVector TSimulationEntity::getForce(){
	return m_TotalForce;
}

tri::TVector TSimulationEntity::getVelocity(){
	NewtonBody* body = NULL;
	tri::TVector vel;
	body  = getPhysicsBody();
	if (body != NULL && m_GraphicComponent != NULL){
		NewtonBodyGetVelocity(body, &vel[0]);
	}
	return vel;
}

void TSimulationEntity::setVelocity(tri::TVector vel){
	NewtonBody* body = NULL;
	body  = getPhysicsBody();
	if (body != NULL && m_GraphicComponent != NULL){
		NewtonBodySetVelocity(body, &vel[0]);
	}
}

int TSimulationEntity::getID(){
	return mID;
}

void TSimulationEntity::clearPhysics(){
	if (m_PhysicsModelComponent!=NULL){
		delete m_PhysicsModelComponent;
		m_PhysicsModelComponent = NULL;
	}
	
	if (m_NewtonWorld!=NULL && m_PhysicsComponent!=NULL){
		NewtonDestroyBody(m_NewtonWorld, m_PhysicsComponent);
	}
	
	m_NewtonWorld = NULL;
	m_PhysicsComponent = NULL;
}

void TSimulationEntity::clearGraphics(){
	if (m_GraphicComponent != NULL && m_OwnerOfGraphics){
		delete m_GraphicComponent;
	}
	m_GraphicComponent = NULL;
}

void TSimulationEntity::freeParent(){
	if (m_Parent!=NULL){
		clearPhysics();
		if (m_GraphicComponent!=NULL){
			m_Parent->removeEntity(m_GraphicComponent);
		}
		m_Parent=NULL;
	}
}

void TSimulationEntity::setParent(TSimulationScene* scene, bool owner){
	if (m_GraphicComponent!=NULL){
		scene->addEntity(m_GraphicComponent);
	}
	setWorld(scene->getPhysicsWorld());
	m_Parent = scene;
	m_OwnedByParent = owner;
}

bool TSimulationEntity::isOwnedByParent(){
	return m_OwnedByParent;
}

TSimulationScene* TSimulationEntity::getParent(){
	return m_Parent;
}

}

